package com.eifesun.monitor.application;

import static java.util.Arrays.asList;

import java.util.Collections;
import java.util.Map;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("modbus.configuration")
public class ModbusConfiguration {
    private static final float DEFAULT_FACTOR = 0.75f;
    @Autowired
    private PropertiesConfiguration configuration;

    private volatile Map<Integer, ModbusClient> modbusClients;

    public int getModbusDispatchPeriod() {
        return configuration.getInt("modbus.group.dispatch.period");
    }

    public Map<Integer, ModbusClient> getModbusClients() {
        return Collections.unmodifiableMap(modbusClients);
    }

    public void refreshModbusClients() {

        final String[] modbusGroups = configuration.getStringArray(getGroupArrayKey());
        if (modbusGroups.length == 0 || "".equals(modbusGroups[0])) {
            modbusClients = Collections.emptyMap();
            return;
        }

        final Map<Integer, ModbusClient> modbusClientMap = Maps.newHashMapWithExpectedSize(modbusGroups.length);
        LOG.debug("get all avaliable clients:" + asList(modbusGroups));

        for (String clientInstance : modbusGroups) {

            final String modbusKey = String.format("modbus.%s", clientInstance);
            final String functionCodeKey = String.format("modbus.%s.functionCode", clientInstance);
            final String deviceTypeKey = String.format("modbus.%s.type", clientInstance);

            final int functionCode = configuration.getInt(functionCodeKey);
            final String deviceType = configuration.getString(deviceTypeKey).toUpperCase();
            final String communicationType = getModbusCommunicationType();

            final String startAddressKey = String.format("%s.block%d.startaddress", modbusKey, functionCode);
            final String quantitykey = String.format("%s.block%d.quantity", modbusKey, functionCode);
            final String unitIdValueKey = String.format("%s.block%d.unitId", modbusKey, functionCode);

            final int startAddressValue = configuration.getInt(startAddressKey);
            final int quantityValue = configuration.getInt(quantitykey);
            final int unitIdValue = configuration.getInt(unitIdValueKey);

            //@formatter:off
            ModbusClient.Builder modbusClientBuilder = new ModbusClient.Builder().
                                        configBasic(clientInstance, deviceType, true).
                                        configRequest(startAddressValue, quantityValue, unitIdValue).
                                        configFunctionCode(functionCode).
                                        configCommunicationType(communicationType);
            //@formatter:on
            ModbusClient modbusClient = modbusClientBuilder.build();
            modbusClientMap.put(unitIdValue, modbusClient);
        }

        modbusClients = modbusClientMap;
    }

    public int getModbusLocalPort() {
        String modbusServerPortKey = String.format("modbus.%s.tcp.local.port", getSelectedGroupName());
        return configuration.getInt(modbusServerPortKey);
    }

    public int getFutureWorkerNumber() {
        String groupArrayKey = getGroupArrayKey();
        String[] groupArray = configuration.getStringArray(groupArrayKey);
        int workerNumber = groupArray.length;
        int futureWorkerNumber = (int) (workerNumber / DEFAULT_FACTOR);
        return futureWorkerNumber;
    }

    private String getGroupArrayKey() {
        return String.format("modbus.group.%s.names", getSelectedGroupName());
    }

    private String getSelectedGroupName() {
        return configuration.getString("modbus.group");
    }

    public String getModbusCommunicationType() {
        String communicationTypeKey = String.format("modbus.%s.communicationMode", getSelectedGroupName());
        return configuration.getString(communicationTypeKey);
    }

    public String getNioCommunicationType() {
        String nioCommunicationTypeKey = String.format("modbus.%s.nioMode", getSelectedGroupName());
        return configuration.getString(nioCommunicationTypeKey);
    }

    public int getNioLocalPort() {
        return configuration.getInt("nio.local.port");
    }

    public int getNioWorkerNumber() {
        return configuration.getInt("nio.client.number");
    }

    public String getModbusServerHost() {
        String modbusClientHostKey = String.format("modbus.%s.tcp.server.host", getSelectedGroupName());
        return configuration.getString(modbusClientHostKey);
    }

    public int getModbusServerPort() {
        String modbusClientPortKey = String.format("modbus.%s.tcp.server.port", getSelectedGroupName());
        return configuration.getInt(modbusClientPortKey);
    }

    public String getModbusSerialPort() {
        String modbusClientSerialPortkey = String.format("modbus.%s.serial.client.port", getSelectedGroupName());
        return configuration.getString(modbusClientSerialPortkey);
    }

    public String getNioServerHost() {
        return configuration.getString("nio.server.host");
    }

    public int getNioServerPort() {
        return configuration.getInt("nio.server.port");
    }

    public void setConfiguration(PropertiesConfiguration configuration) {
        this.configuration = configuration;
    }

}
